package com.jwong.example.service.configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.CacheBuilder;
import com.jwong.example.service.model.Order;
import com.jwong.example.service.model.Person;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import org.springframework.boot.actuate.metrics.web.servlet.DefaultWebMvcTagsProvider;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;

/**
 * 如何基于Response添加自定义tags
 */
@Configuration
public class HttpMetricsTagConfiguration {

    private final Map<HttpServletResponse, Tags> responseTags = CacheBuilder.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofSeconds(10))
            .<HttpServletResponse, Tags>build()
            .asMap();

    /**
     * 注入一个过滤器，该过滤器从Response中提取信息，并构造一个tag，将该tag放入Cache
     */
    @Bean
    OncePerRequestFilter extractCountry() {
        return new OncePerRequestFilter() {
            private final ObjectMapper mapper = new ObjectMapper();

            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain filterChain) throws ServletException, IOException {
                ContentCachingResponseWrapper cached = new ContentCachingResponseWrapper(response);
                filterChain.doFilter(request, cached);

                Object path = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
                if (path.equals("/api/person/{id}")) {
                    // Prometheus要求所有的“http.server.requests”都使用相同的标签(e.g http_server_requests_seconds)。
                    // 所以我们需要添加一个@Timed("person.requests")到/api/person/{id}端点，
                    // 这样它就有一个不同的名字(e.g person_requests_seconds_count )，
                    Person person = mapper.readValue(cached.getContentAsByteArray(), Person.class);
                    responseTags.put(response, Tags.of("country", person.getCountry()));
                }

                cached.copyBodyToResponse();
            }
        };
    }

    /**
     *  该类由org.springframework.boot.actuate.metrics.web.servlet提供
     *  为基于Spring mvc的请求处理提供Tag。
     *  处理由 OncePerRequestFilter 处理完成的 Tags: 将旧的Tags与新Tags合并为一个新的Tags(Tags是一个Iterable)
     */
    @Bean
    WebMvcTagsProvider webMvcTagsProvider() {
        return new DefaultWebMvcTagsProvider() {
            @Override
            public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
                                         Object handler, Throwable exception) {
                return Tags.concat(
                        super.getTags(request, response, handler, exception),
                        Optional.ofNullable(responseTags.remove(response)).orElse(Tags.empty())
                );
            }
        };
    }

}